package com.lixueju.security.box.web.weixin;

import com.alibaba.fastjson.JSONObject;
import com.lixueju.security.box.core.exception.SecurityBoxException;
import com.lixueju.security.box.core.model.request.WeixinDecodeLoginRequest;
import com.lixueju.security.box.core.model.request.WeixinLoginRequest;
import com.lixueju.security.box.core.properties.SecurityBoxProperties;
import com.lixueju.security.box.core.properties.WeixinAppProperties;
import com.lixueju.security.box.core.properties.WeixinCodeType;
import com.lixueju.security.box.web.weixin.model.*;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.http.client.SimpleClientHttpRequestFactory;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;

import java.io.UnsupportedEncodingException;
import java.util.concurrent.TimeUnit;

/**
 * @author lixueju
 * @since 2019/10/11 16:28
 **/
@Service
public class WeixinAuthService {
    @Autowired
    private SecurityBoxProperties securityBoxProperties;

    @Autowired
    StringRedisTemplate stringRedisTemplate;


    public CodeResponse login(WeixinLoginRequest weixinLoginRequest) {
        if (StringUtils.isBlank(weixinLoginRequest.getAppName())) {
            throw new SecurityBoxException("appName不能为空");
        }
        String codeType = securityBoxProperties.getWeixin().getAppByName(weixinLoginRequest.getAppName()).getCodeType();
        if (codeType.equals(WeixinCodeType.oauth2.name())) {
            return getOauthAccessToken(weixinLoginRequest);
        }
        if (codeType.equals(WeixinCodeType.jscode.name())) {
            return jscode2session(weixinLoginRequest);
        }
        throw new SecurityBoxException("请配置微信的codeType为：‘" + WeixinCodeType.oauth2 + "’或者‘" + WeixinCodeType.jscode + "’");
    }

    /**
     * 适合小程序登录（需要传递密文，解析用户详细信息，包括UnionID）
     */
    public WeixinUserInfo loginOfDecode(WeixinDecodeLoginRequest weixinDecodeLoginRequest) {
        //根据code获取session信息
        Code2sessionResponse code2sessionResponse = jscode2session(weixinDecodeLoginRequest);
        // 微信的配置信息对象
        WeixinAppProperties weixinAppProperties = securityBoxProperties.getWeixin().getAppByName(weixinDecodeLoginRequest.getAppName());
        //根据session解密用户的加密数据
        WeixinBizDataCrypt wxBizDataCrypt = new WeixinBizDataCrypt(weixinAppProperties.getAppid(), code2sessionResponse.getSessionKey());
        return wxBizDataCrypt.decryptDataForUserInfo(weixinDecodeLoginRequest.getEncryptedData(), weixinDecodeLoginRequest.getIv());
    }

    /**
     * 解析手机号
     */
    public WeixinPhoneNumber getWeixinPhoneNumber(WeixinDecodeLoginRequest weixinDecodeLoginRequest){
        //根据code获取session信息
        Code2sessionResponse code2sessionResponse = jscode2session(weixinDecodeLoginRequest);
        // 微信的配置信息对象
        WeixinAppProperties weixinAppProperties = securityBoxProperties.getWeixin().getAppByName(weixinDecodeLoginRequest.getAppName());
        //根据session解密用户的加密数据
        WeixinBizDataCrypt wxBizDataCrypt = new WeixinBizDataCrypt(weixinAppProperties.getAppid(), code2sessionResponse.getSessionKey());
        return wxBizDataCrypt.decryptDataForPhoneNumber(weixinDecodeLoginRequest.getEncryptedData(), weixinDecodeLoginRequest.getIv());
    }

    /**
     * 适合小程序登录（简单登录，只获取openId）
     */
    public Code2sessionResponse jscode2session(WeixinLoginRequest weixinLoginRequest) {
        RestTemplate restTemplate1 = getRestTemplate();
        String url = "https://api.weixin.qq.com/sns/jscode2session?appid={1}&secret={2}&js_code={3}&grant_type={4}";
        String appid = securityBoxProperties.getWeixin().getAppByName(weixinLoginRequest.getAppName()).getAppid();
        String secret = securityBoxProperties.getWeixin().getAppByName(weixinLoginRequest.getAppName()).getSecret();
        String jsonStr = restTemplate1.getForObject(url, String.class, appid, secret, weixinLoginRequest.getCode(), "authorization_code");
        return JSONObject.parseObject(jsonStr, Code2sessionResponse.class);
    }


    /**
     * 基于Oauth2协议授权码模式登录。适合移动应用和网站应用。
     */
    public OauthAccessTokenResponse getOauthAccessToken(WeixinLoginRequest weixinLoginRequest) {
        RestTemplate restTemplateNew = new RestTemplate();
        String appid = securityBoxProperties.getWeixin().getAppByName(weixinLoginRequest.getAppName()).getAppid();
        String secret = securityBoxProperties.getWeixin().getAppByName(weixinLoginRequest.getAppName()).getSecret();
        String url = "https://api.weixin.qq.com/sns/oauth2/access_token?appid={1}&secret={2}&code={3}&grant_type=authorization_code";
        String jsonStr = restTemplateNew.getForObject(url, String.class, appid, secret, weixinLoginRequest.getCode());
        return JSONObject.parseObject(jsonStr, OauthAccessTokenResponse.class);
    }

    /**
     * 获取微信用户信息
     *
     * @return
     */
    public WeixinUserInfo getUserInfo(OauthAccessTokenResponse oauthAccessTokenResponse) {
        String url = "https://api.weixin.qq.com/sns/userinfo?access_token=" + oauthAccessTokenResponse.getAccessToken() + "&openid=" + oauthAccessTokenResponse.getOpenid();
        RestTemplate restTemplate1 = getRestTemplate();
        String jsonStr = restTemplate1.getForObject(url, String.class);
        JSONObject jsonObject = null;
        try {
            jsonObject = JSONObject.parseObject(new String(jsonStr.getBytes("ISO-8859-1"),"utf-8"));
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
            throw new SecurityBoxException("获取微信信息解码失败");
        }
        WeixinUserInfo userInfo = new WeixinUserInfo();
        userInfo.setOpenId(jsonObject.getString("openid"));
        userInfo.setUnionId(jsonObject.getString("unionid"));
        userInfo.setNickName(jsonObject.getString("nickname"));
        userInfo.setAvatarUrl(jsonObject.getString("headimgurl"));
        userInfo.setGender(jsonObject.getInteger("sex"));
        userInfo.setProvince(jsonObject.getString("province"));
        userInfo.setCity(jsonObject.getString("city"));
        return userInfo;
    }

    /**
     * https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=APPSECRET
     * 1. 获取微信小程序、公众号等的全局唯一接口调用凭据
     * 2. 调调用绝大多数接口时都需使用该 access_token
     * 3. 该access_token不是调用userinfo接口所需的access_token
     * 4. 可以调用info接口，但是需要关注公众号
     */
    public String getBaseAccessToken(String appid, String secret) {
        String accessToken = stringRedisTemplate.opsForValue().get(securityBoxProperties.getWeixin().getWeixinAccessTokenKeyForRedis());
        if (accessToken == null) {
            RestTemplate restTemplateNew = new RestTemplate();
            String url = "https://api.weixin.qq.com/cgi-bin/token?grant_type={1}&appid={2}&secret={3}";
            String accessTokenResultStr = restTemplateNew.getForObject(url, String.class, "client_creUdential", appid, secret);
            JSONObject tokenJson = JSONObject.parseObject(accessTokenResultStr);
            accessToken = tokenJson.getString("access_token");
            if (accessToken == null) {
                throw new SecurityBoxException(tokenJson.getString("errmsg"));
            } else {
                stringRedisTemplate.boundValueOps(securityBoxProperties.getWeixin().getWeixinAccessTokenKeyForRedis()).set(accessToken, tokenJson.getIntValue("expires_in") - 200, TimeUnit.SECONDS);
            }
        }
        return accessToken;
    }

    private RestTemplate getRestTemplate() {
        SimpleClientHttpRequestFactory requestFactory = new SimpleClientHttpRequestFactory();
        requestFactory.setConnectTimeout(3000);
        requestFactory.setReadTimeout(3000);
        return new RestTemplate(requestFactory);
    }
}
